/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(non_upper_case_globals, unused_assignments, unused_mut)]

use crate::common::*;
extern "C" {
    fn fclose(__stream: *mut FILE) -> libc::c_int;
    fn fopen(_: *const libc::c_char, _: *const libc::c_char) -> *mut FILE;
    fn free(_: *mut libc::c_void);
    fn strtok_r(
        __s: *mut libc::c_char,
        __delim: *const libc::c_char,
        __save_ptr: *mut *mut libc::c_char,
    ) -> *mut libc::c_char;
    fn strncasecmp(_: *const libc::c_char, _: *const libc::c_char, _: libc::c_ulong)
        -> libc::c_int;
    fn strcasecmp(_: *const libc::c_char, _: *const libc::c_char) -> libc::c_int;
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn sudo_debug_exit_ptr_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: *const libc::c_void,
    );
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn sudo_parseln_v2(
        buf: *mut *mut libc::c_char,
        bufsize: *mut size_t,
        lineno: *mut libc::c_uint,
        fp: *mut FILE,
        flags: libc::c_int,
    ) -> ssize_t;
    fn sudo_debug_exit_bool_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        subsys: libc::c_int,
        ret: bool,
    );
    static mut sudo_nss_file: sudo_nss;
    static mut sudo_nss_ldap: sudo_nss;
    static mut sudo_nss_sss: sudo_nss;
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_parse_tree {
    pub userspecs: userspec_list,
    pub defaults: defaults_list,
    pub aliases: *mut rbtree,
    pub shost: *const libc::c_char,
    pub lhost: *const libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct userspec_list {
    pub tqh_first: *mut userspec,
    pub tqh_last: *mut *mut userspec,
}

// 多处定义，后期统一处理
#[derive(Copy, Clone)]
#[repr(C)]
pub struct userspec {
    pub entries: C2RustUnnamed_4,
    pub users: member_list,
    pub privileges: privilege_list,
    pub comments: comment_list,
    pub lineno: libc::c_int,
    pub file: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct comment_list {
    pub stqh_first: *mut sudoers_comment,
    pub stqh_last: *mut *mut sudoers_comment,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudoers_comment {
    pub entries: C2RustUnnamed_1,
    pub str_0: *mut libc::c_char,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_1 {
    pub stqe_next: *mut sudoers_comment,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct privilege_list {
    pub tqh_first: *mut privilege,
    pub tqh_last: *mut *mut privilege,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct privilege {
    pub entries: C2RustUnnamed_3,
    pub ldap_role: *mut libc::c_char,
    pub hostlist: member_list,
    pub cmndlist: cmndspec_list,
    pub defaults: defaults_list,
}

// 多处定义，后期统一处理
#[derive(Copy, Clone)]
#[repr(C)]
pub struct cmndspec_list {
    pub tqh_first: *mut cmndspec,
    pub tqh_last: *mut *mut cmndspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct cmndspec {
    pub entries: C2RustUnnamed_2,
    pub runasuserlist: *mut member_list,
    pub runasgrouplist: *mut member_list,
    pub cmnd: *mut member,
    pub tags: cmndtag,
    pub timeout: libc::c_int,
    pub notbefore: time_t,
    pub notafter: time_t,
    pub role: *mut libc::c_char,
    pub type_0: *mut libc::c_char,
}
#[derive(Copy, Clone, BitfieldStruct)]
#[repr(C)]
pub struct cmndtag {
    #[bitfield(name = "nopasswd", ty = "libc::c_int", bits = "0..=2")]
    #[bitfield(name = "noexec", ty = "libc::c_int", bits = "3..=5")]
    #[bitfield(name = "setenv", ty = "libc::c_int", bits = "6..=8")]
    #[bitfield(name = "log_input", ty = "libc::c_int", bits = "9..=11")]
    #[bitfield(name = "log_output", ty = "libc::c_int", bits = "12..=14")]
    #[bitfield(name = "send_mail", ty = "libc::c_int", bits = "15..=17")]
    #[bitfield(name = "follow", ty = "libc::c_int", bits = "18..=20")]
    pub nopasswd_noexec_setenv_log_input_log_output_send_mail_follow: [u8; 3],
    #[bitfield(padding)]
    pub c2rust_padding: [u8; 1],
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_2 {
    pub tqe_next: *mut cmndspec,
    pub tqe_prev: *mut *mut cmndspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_3 {
    pub tqe_next: *mut privilege,
    pub tqe_prev: *mut *mut privilege,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_4 {
    pub tqe_next: *mut userspec,
    pub tqe_prev: *mut *mut userspec,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_nss_list {
    pub tqh_first: *mut sudo_nss,
    pub tqh_last: *mut *mut sudo_nss,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct sudo_nss {
    pub entries: C2RustUnnamed_5,
    pub open: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub close: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub parse: Option<unsafe extern "C" fn(*mut sudo_nss) -> *mut sudoers_parse_tree>,
    pub query: Option<unsafe extern "C" fn(*mut sudo_nss, *mut passwd) -> libc::c_int>,
    pub getdefs: Option<unsafe extern "C" fn(*mut sudo_nss) -> libc::c_int>,
    pub handle: *mut libc::c_void,
    pub parse_tree: *mut sudoers_parse_tree,
    pub ret_if_found: bool,
    pub ret_if_notfound: bool,
}
#[derive(Copy, Clone)]
#[repr(C)]
pub struct C2RustUnnamed_5 {
    pub tqe_next: *mut sudo_nss,
    pub tqe_prev: *mut *mut sudo_nss,
}
static mut snl: sudo_nss_list = sudo_nss_list {
    tqh_first: 0 as *const sudo_nss as *mut sudo_nss,
    tqh_last: 0 as *const *mut sudo_nss as *mut *mut sudo_nss,
};

/* Make sure we have not already inserted the nss entry. */
#[macro_export]
macro_rules! SUDO_NSS_CHECK_UNUSED {
    ($nss:expr, $tag:expr, $cp:expr, $last:expr) => {
        if !($nss.entries.tqe_next).is_null() || !($nss.entries.tqe_prev).is_null() {
            sudo_warnx!(
                b"internal error: nsswitch entry \"%s\" already in use\0" as *const u8
                    as *const libc::c_char,
                $tag
            );
            $cp = strtok_r(
                0 as *mut libc::c_char,
                b" \t\0" as *const u8 as *const libc::c_char,
                &mut $last,
            );
            continue;
        };
    };
}

/*
 * Read in /etc/nsswitch.conf
 * Returns a tail queue of matches.
 */
#[no_mangle]
pub fn sudo_read_nss() -> *mut sudo_nss_list {
    let mut fp: *mut FILE = std::ptr::null_mut::<FILE>();
    let mut line: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
    let mut linesize: size_t = 0 as libc::c_int as size_t;
    let mut saw_sss: bool = false;
    let mut saw_ldap: bool = false;
    let mut saw_files: bool = false;
    let mut got_match: bool = false;

    unsafe {
        debug_decl!(SUDOERS_DEBUG_NSS!());
    }

    unsafe {
        fp = fopen(
            _PATH_NSSWITCH_CONF!(),
            b"r\0" as *const u8 as *const libc::c_char,
        );

        if !fp.is_null() {
            while sudo_parseln_v2(
                &mut line,
                &mut linesize,
                std::ptr::null_mut::<libc::c_uint>(),
                fp,
                0 as libc::c_int,
            ) != -(1 as libc::c_int) as libc::c_long
            {
                let mut cp: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();
                let mut last: *mut libc::c_char = std::ptr::null_mut::<libc::c_char>();

                /* Skip blank or comment lines */
                if *line as libc::c_int == '\0' as i32 {
                    continue;
                }

                /* Look for a line starting with "sudoers:" */
                if strncasecmp(
                    line,
                    b"sudoers:\0" as *const u8 as *const libc::c_char,
                    8 as libc::c_int as libc::c_ulong,
                ) != 0 as libc::c_int
                {
                    continue;
                }

                /* Parse line */
                cp = strtok_r(
                    line.offset(8 as libc::c_int as isize),
                    b" \t\0" as *const u8 as *const libc::c_char,
                    &mut last,
                );

                while !cp.is_null() {
                    if strcasecmp(cp, b"files\0" as *const u8 as *const libc::c_char)
                        == 0 as libc::c_int
                        && !saw_files
                    {
                        SUDO_NSS_CHECK_UNUSED!(
                            sudo_nss_file,
                            b"files\0" as *const u8 as *const libc::c_char,
                            cp,
                            last
                        );

                        sudo_nss_file.entries.tqe_next = std::ptr::null_mut::<sudo_nss>();
                        sudo_nss_file.entries.tqe_prev = snl.tqh_last;
                        *snl.tqh_last = &mut sudo_nss_file;
                        snl.tqh_last = &mut sudo_nss_file.entries.tqe_next;

                        saw_files = true;
                        got_match = saw_files;
                    } else if strcasecmp(cp, b"ldap\0" as *const u8 as *const libc::c_char)
                        == 0 as libc::c_int
                        && !saw_ldap
                    {
                        SUDO_NSS_CHECK_UNUSED!(
                            sudo_nss_ldap,
                            b"ldap\0" as *const u8 as *const libc::c_char,
                            cp,
                            last
                        );

                        sudo_nss_ldap.entries.tqe_next = std::ptr::null_mut::<sudo_nss>();
                        sudo_nss_ldap.entries.tqe_prev = snl.tqh_last;
                        *snl.tqh_last = &mut sudo_nss_ldap;
                        snl.tqh_last = &mut sudo_nss_ldap.entries.tqe_next;
                        saw_ldap = 1 as libc::c_int != 0;
                        got_match = saw_ldap;
                    } else if strcasecmp(cp, b"sss\0" as *const u8 as *const libc::c_char)
                        == 0 as libc::c_int
                        && !saw_sss
                    {
                        SUDO_NSS_CHECK_UNUSED!(
                            sudo_nss_sss,
                            b"sss\0" as *const u8 as *const libc::c_char,
                            cp,
                            last
                        );
                        sudo_nss_sss.entries.tqe_next = std::ptr::null_mut::<sudo_nss>();
                        sudo_nss_sss.entries.tqe_prev = snl.tqh_last;
                        *snl.tqh_last = &mut sudo_nss_sss;
                        snl.tqh_last = &mut sudo_nss_sss.entries.tqe_next;
                        saw_sss = 1 as libc::c_int != 0;
                        got_match = saw_sss;
                    } else if strcasecmp(
                        cp,
                        b"[NOTFOUND=return]\0" as *const u8 as *const libc::c_char,
                    ) == 0 as libc::c_int
                        && got_match as libc::c_int != 0
                    {
                        /* NOTFOUND affects the most recent entry */
                        (**(*(snl.tqh_last as *mut sudo_nss_list)).tqh_last).ret_if_notfound =
                            1 as libc::c_int != 0;
                        got_match = false;
                    } else if strcasecmp(
                        cp,
                        b"[SUCCESS=return]\0" as *const u8 as *const libc::c_char,
                    ) == 0 as libc::c_int
                        && got_match as libc::c_int != 0
                    {
                        /* SUCCESS affects the most recent entry */
                        (**(*(snl.tqh_last as *mut sudo_nss_list)).tqh_last).ret_if_found =
                            1 as libc::c_int != 0;
                        got_match = false;
                    } else {
                        got_match = false;
                    }
                    cp = strtok_r(
                        std::ptr::null_mut::<libc::c_char>(),
                        b" \t\0" as *const u8 as *const libc::c_char,
                        &mut last,
                    );
                }
                /* Only parse the first "sudoers:" line */
                break;
            }
            free(line as *mut libc::c_void);
            fclose(fp);
        }

        // nomatch:
        /* Default to files only if no matches */
        if (snl.tqh_first).is_null() {
            sudo_nss_file.entries.tqe_next = std::ptr::null_mut::<sudo_nss>();
            sudo_nss_file.entries.tqe_prev = snl.tqh_last;
            *snl.tqh_last = &mut sudo_nss_file;
            snl.tqh_last = &mut sudo_nss_file.entries.tqe_next;
        }
    } //unsafe

    unsafe {
        debug_return_ptr!(&mut snl as *mut sudo_nss_list);
    }
}

#[no_mangle]
pub fn sudo_nss_can_continue(mut nss: *mut sudo_nss, mut match_0: libc::c_int) -> bool {
    unsafe {
        debug_decl!(SUDOERS_DEBUG_NSS!());
    }

    /* Handle [NOTFOUND=return] */
    unsafe {
        if (*nss).ret_if_notfound as libc::c_int != 0 && match_0 == UNSPEC {
            debug_return_bool!(false);
        }

        /* Handle [SUCCESS=return] */
        if (*nss).ret_if_found as libc::c_int != 0 && match_0 != UNSPEC {
            debug_return_bool!(false);
        }
    } //unsafe

    unsafe {
        debug_return_bool!(true);
    }
}

unsafe extern "C" fn run_static_initializers() {
    snl = {
        sudo_nss_list {
            //tqh_first: 0 as *const sudo_nss as *mut sudo_nss,
            tqh_first: std::ptr::null_mut::<sudo_nss>(),
            tqh_last: &snl.tqh_first as *const *mut sudo_nss as *mut *mut sudo_nss,
        }
        //  init
    };
}
#[used]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_os = "windows", link_section = ".CRT$XIB")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
static INIT_ARRAY: [unsafe extern "C" fn(); 1] = [run_static_initializers];
